`timescale 1 ns / 1 ps 
/**
 ******************************************************************************
 * @file    jtag_cm_process.v
 * @author  KEN
 * @version V2.0
 * @date    Jul. 2nd, 2020
 * @brief   Convert JTAG vectors to signals
 ******************************************************************************
 * @attention
 *
 * <h2><center>&copy; COPYRIGHT 2020 K'sP</center></h2>
 ******************************************************************************
 */

module jtag_lm_process
	   #
	   (
		   parameter integer C_JTAG_TYPE = 0, //0=normal, 1=with tri io control
		   parameter integer C_JTAG_TRI_POLARITY = 0, //0=<low as input, high as output> 1=<high as input, low as output>

		   parameter integer C_VECTOR_LEN = 128,

		   parameter integer C_AXI_MAX_BURST_LENGTH = 256,
		   parameter integer C_AXI_ADDR_WIDTH = 32,
		   parameter integer C_AXI_DATA_WIDTH = 64
	   )
	   (
		   //Global ACLK
		   (* X_INTERFACE_INFO = "xilinx.com:signal:clock:1.0 m_jtag_aclk CLK" *)
		   (* X_INTERFACE_PARAMETER = "ASSOCIATED_BUSIF m_jtag, ASSOCIATED_RESET m_jtag_aresetn" *)
		   input wire m_jtag_aclk,

		   //Global ARESETn
		   (* X_INTERFACE_INFO = "xilinx.com:signal:reset:1.0 m_jtag_aresetn RST" *)
		   (* X_INTERFACE_PARAMETER = "POLARITY ACTIVE_LOW" *)
		   input wire m_jtag_aresetn,

		   input wire ENABLE,
		   output wire BUSY,

		   input wire [31: 0] LENGTH,
		   input wire [31: 0] FREQ_WORD,
		   input wire [31: 0] TMS_MEM_ADDR,
		   input wire [31: 0] TDI_MEM_ADDR,
		   input wire [31: 0] TDO_MEM_ADDR,

		   //JTAG
		   (* X_INTERFACE_INFO = "xilinx.com:interface:jtag:2.0 m_jtag TCK" *)
		   output wire m_tck,
		   (* X_INTERFACE_INFO = "xilinx.com:interface:jtag:2.0 m_jtag TMS" *)
		   output wire m_tms,
		   (* X_INTERFACE_INFO = "xilinx.com:interface:jtag:2.0 m_jtag TDI" *)
		   output wire m_tdi,
		   (* X_INTERFACE_INFO = "xilinx.com:interface:jtag:2.0 m_jtag TDO" *)
		   input wire m_tdo,

		   (* X_INTERFACE_INFO = "CSIC.com.cn:user:jtag:1.0 m_jtag_io TCK_IO" *)
		   inout wire m_tck_io,
		   (* X_INTERFACE_INFO = "CSIC.com.cn:user:jtag:1.0 m_jtag_io TCK_T" *)
		   output wire m_tck_t,
		   (* X_INTERFACE_INFO = "CSIC.com.cn:user:jtag:1.0 m_jtag_io TMS_IO" *)
		   inout wire m_tms_io,
		   (* X_INTERFACE_INFO = "CSIC.com.cn:user:jtag:1.0 m_jtag_io TMS_T" *)
		   output wire m_tms_t,
		   (* X_INTERFACE_INFO = "CSIC.com.cn:user:jtag:1.0 m_jtag_io TDI_IO" *)
		   inout wire m_tdi_io,
		   (* X_INTERFACE_INFO = "CSIC.com.cn:user:jtag:1.0 m_jtag_io TDI_T" *)
		   output wire m_tdi_t,
		   (* X_INTERFACE_INFO = "CSIC.com.cn:user:jtag:1.0 m_jtag_io TDO_IO" *)
		   inout wire m_tdo_io,
		   (* X_INTERFACE_INFO = "CSIC.com.cn:user:jtag:1.0 m_jtag_io TDO_T" *)
		   output wire m_tdo_t,

		   //Global ACLK
		   (* X_INTERFACE_INFO = "xilinx.com:signal:clock:1.0 m_axi_aclk CLK" *)
		   (* X_INTERFACE_PARAMETER = "ASSOCIATED_BUSIF m_axi, ASSOCIATED_RESET m_axi_aresetn" *)
		   input wire m_axi_aclk,

		   //Global ARESETn
		   (* X_INTERFACE_INFO = "xilinx.com:signal:reset:1.0 m_axi_aresetn RST" *)
		   (* X_INTERFACE_PARAMETER = "POLARITY ACTIVE_LOW" *)
		   input wire m_axi_aresetn,

		   //Write address channel.
		   (* X_INTERFACE_INFO = "xilinx.com:interface:aximm:1.0 m_axi AWADDR" *)
		   output wire [(C_AXI_ADDR_WIDTH - 1): 0] m_axi_awaddr,
		   (* X_INTERFACE_INFO = "xilinx.com:interface:aximm:1.0 m_axi AWLEN" *)
		   output wire [7: 0] m_axi_awlen,
		   (* X_INTERFACE_INFO = "xilinx.com:interface:aximm:1.0 m_axi AWSIZE" *)
		   output wire [2: 0] m_axi_awsize,
		   (* X_INTERFACE_INFO = "xilinx.com:interface:aximm:1.0 m_axi AWBURST" *)
		   output wire [1: 0] m_axi_awburst,
		   (* X_INTERFACE_INFO = "xilinx.com:interface:aximm:1.0 m_axi AWLOCK" *)
		   output wire m_axi_awlock,
		   (* X_INTERFACE_INFO = "xilinx.com:interface:aximm:1.0 m_axi AWCACHE" *)
		   output wire [3: 0] m_axi_awcache,
		   (* X_INTERFACE_INFO = "xilinx.com:interface:aximm:1.0 m_axi AWPROT" *)
		   output wire [2: 0] m_axi_awprot,
		   (* X_INTERFACE_INFO = "xilinx.com:interface:aximm:1.0 m_axi AWQOS" *)
		   output wire [3: 0] m_axi_awqos,
		   (* X_INTERFACE_INFO = "xilinx.com:interface:aximm:1.0 m_axi AWVALID" *)
		   output wire m_axi_awvalid,
		   (* X_INTERFACE_INFO = "xilinx.com:interface:aximm:1.0 m_axi AWREADY" *)
		   input wire m_axi_awready,

		   //Write data channel.
		   (* X_INTERFACE_INFO = "xilinx.com:interface:aximm:1.0 m_axi WDATA" *)
		   output wire [(C_AXI_DATA_WIDTH - 1): 0] m_axi_wdata,
		   (* X_INTERFACE_INFO = "xilinx.com:interface:aximm:1.0 m_axi WSTRB" *)
		   output wire [(C_AXI_DATA_WIDTH / 8 - 1): 0] m_axi_wstrb,
		   (* X_INTERFACE_INFO = "xilinx.com:interface:aximm:1.0 m_axi WLAST" *)
		   output wire m_axi_wlast,
		   (* X_INTERFACE_INFO = "xilinx.com:interface:aximm:1.0 m_axi WVALID" *)
		   output wire m_axi_wvalid,
		   (* X_INTERFACE_INFO = "xilinx.com:interface:aximm:1.0 m_axi WREADY" *)
		   input wire m_axi_wready,

		   //Write response channel.
		   (* X_INTERFACE_INFO = "xilinx.com:interface:aximm:1.0 m_axi BRESP" *)
		   input wire [1: 0] m_axi_bresp,
		   (* X_INTERFACE_INFO = "xilinx.com:interface:aximm:1.0 m_axi BVALID" *)
		   input wire m_axi_bvalid,
		   (* X_INTERFACE_INFO = "xilinx.com:interface:aximm:1.0 m_axi BREADY" *)
		   output wire m_axi_bready,

		   //Read address channel.
		   (* X_INTERFACE_INFO = "xilinx.com:interface:aximm:1.0 m_axi ARADDR" *)
		   output wire [(C_AXI_ADDR_WIDTH - 1): 0] m_axi_araddr,
		   (* X_INTERFACE_INFO = "xilinx.com:interface:aximm:1.0 m_axi ARLEN" *)
		   output wire [7: 0] m_axi_arlen,
		   (* X_INTERFACE_INFO = "xilinx.com:interface:aximm:1.0 m_axi ARSIZE" *)
		   output wire [2: 0] m_axi_arsize,
		   (* X_INTERFACE_INFO = "xilinx.com:interface:aximm:1.0 m_axi ARBURST" *)
		   output wire [1: 0] m_axi_arburst,
		   (* X_INTERFACE_INFO = "xilinx.com:interface:aximm:1.0 m_axi ARLOCK" *)
		   output wire m_axi_arlock,
		   (* X_INTERFACE_INFO = "xilinx.com:interface:aximm:1.0 m_axi ARCACHE" *)
		   output wire [3: 0] m_axi_arcache,
		   (* X_INTERFACE_INFO = "xilinx.com:interface:aximm:1.0 m_axi ARPROT" *)
		   output wire [2: 0] m_axi_arprot,
		   (* X_INTERFACE_INFO = "xilinx.com:interface:aximm:1.0 m_axi ARQOS" *)
		   output wire [3: 0] m_axi_arqos,
		   (* X_INTERFACE_INFO = "xilinx.com:interface:aximm:1.0 m_axi ARVALID" *)
		   output wire m_axi_arvalid,
		   (* X_INTERFACE_INFO = "xilinx.com:interface:aximm:1.0 m_axi ARREADY" *)
		   input wire m_axi_arready,

		   //Read data channel.
		   (* X_INTERFACE_INFO = "xilinx.com:interface:aximm:1.0 m_axi RDATA" *)
		   input wire [(C_AXI_DATA_WIDTH - 1): 0] m_axi_rdata,
		   (* X_INTERFACE_INFO = "xilinx.com:interface:aximm:1.0 m_axi RRESP" *)
		   input wire [1: 0] m_axi_rresp,
		   (* X_INTERFACE_INFO = "xilinx.com:interface:aximm:1.0 m_axi RLAST" *)
		   input wire m_axi_rlast,
		   (* X_INTERFACE_INFO = "xilinx.com:interface:aximm:1.0 m_axi RVALID" *)
		   input wire m_axi_rvalid,
		   (* X_INTERFACE_INFO = "xilinx.com:interface:aximm:1.0 m_axi RREADY" *)
		   output wire m_axi_rready
	   );

reg busy_r;
assign BUSY = busy_r;

/********************************************************************/
//JTAG Process Instance Start
/********************************************************************/
wire JTAG_ENABLE;
wire JTAG_BUSY; //Global Wire
wire [31: 0] JTAG_LENGTH;
wire [(C_VECTOR_LEN - 1): 0] JTAG_TMS_VECTOR;
wire [(C_VECTOR_LEN - 1): 0] JTAG_TDI_VECTOR;
wire [(C_VECTOR_LEN - 1): 0] JTAG_TDO_VECTOR; //Global Wire

jtag_process
	#
	(
		.C_JTAG_TYPE(C_JTAG_TYPE),
		.C_JTAG_TRI_POLARITY(C_JTAG_TRI_POLARITY),
		.C_VECTOR_LEN(C_VECTOR_LEN)
	)
	jtag_proccess_inst
	(
		.m_jtag_aclk(m_jtag_aclk),
		.m_jtag_aresetn(m_jtag_aresetn),

		.ENABLE(JTAG_ENABLE),
		.BUSY(JTAG_BUSY),

		.LENGTH(JTAG_LENGTH),
		.FREQ_WORD(FREQ_WORD << 1),

		.TMS_VECTOR(JTAG_TMS_VECTOR),
		.TDI_VECTOR(JTAG_TDI_VECTOR),
		.TDO_VECTOR(JTAG_TDO_VECTOR),

		.m_tck(m_tck),
		.m_tms(m_tms),
		.m_tdi(m_tdi),
		.m_tdo(m_tdo),

		.m_tck_io(m_tck_io),
		.m_tms_io(m_tms_io),
		.m_tdi_io(m_tdi_io),
		.m_tdo_io(m_tdo_io),

		.m_tck_t(m_tck_t),
		.m_tms_t(m_tms_t),
		.m_tdi_t(m_tdi_t),
		.m_tdo_t(m_tdo_t)
	);

reg jtag_enable_r;
reg [(C_VECTOR_LEN - 1): 0] jtag_tms_vector_r;
reg [(C_VECTOR_LEN - 1): 0] jtag_tdi_vector_r;

assign JTAG_ENABLE = jtag_enable_r;
assign JTAG_TMS_VECTOR = jtag_tms_vector_r;
assign JTAG_TDI_VECTOR = jtag_tdi_vector_r;

/********************************************************************/
//JTAG Process Instance End
/********************************************************************/

/********************************************************************/
//AXI Circular Memory Access Instance Start
/********************************************************************/
wire AXI_MEM_WENABLE;
wire AXI_MEM_WBUSY; //Global Wire
wire AXI_MEM_WERR; //Global Wire
wire AXI_MEM_RENABLE;
wire AXI_MEM_RBUSY; //Global Wire
wire AXI_MEM_RERR; //Global Wire

wire [(C_AXI_ADDR_WIDTH - 1): 0] AXI_MEM_WADDR_BASE;
wire [(C_AXI_ADDR_WIDTH - 1): 0] AXI_MEM_RADDR_BASE;
wire [31: 0] AXI_MEM_WLEN;
wire [31: 0] AXI_MEM_RLEN;
wire [(C_VECTOR_LEN - 1): 0] AXI_MEM_WVAL;
wire [(C_VECTOR_LEN - 1): 0] AXI_MEM_RVAL; //Global Wire

template_m_axi_s
	#
	(
		.C_MEM_BUFFER_SIZE(C_VECTOR_LEN / 8),
		.C_AXI_MAX_BURST_LENGTH(C_AXI_MAX_BURST_LENGTH),
		.C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
		.C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH)
	)
	m_axi_s_inst
	(
		.WENABLE(AXI_MEM_WENABLE),
		.WBUSY(AXI_MEM_WBUSY),
		.WERR(AXI_MEM_WERR),
		.RENABLE(AXI_MEM_RENABLE),
		.RBUSY(AXI_MEM_RBUSY),
		.RERR(AXI_MEM_RERR),

		.MEM_WADDR_BASE(AXI_MEM_WADDR_BASE),
		.MEM_RADDR_BASE(AXI_MEM_RADDR_BASE),
		.MEM_WLEN(AXI_MEM_WLEN),
		.MEM_RLEN(AXI_MEM_RLEN),
		.MEM_WVAL(AXI_MEM_WVAL),
		.MEM_RVAL(AXI_MEM_RVAL),

		.m_axi_aclk(m_axi_aclk),
		.m_axi_aresetn(m_axi_aresetn),
		.m_axi_awaddr(m_axi_awaddr),
		.m_axi_awlen(m_axi_awlen),
		.m_axi_awsize(m_axi_awsize),
		.m_axi_awburst(m_axi_awburst),
		.m_axi_awlock(m_axi_awlock),
		.m_axi_awcache(m_axi_awcache),
		.m_axi_awprot(m_axi_awprot),
		.m_axi_awqos(m_axi_awqos),
		.m_axi_awvalid(m_axi_awvalid),
		.m_axi_awready(m_axi_awready),
		.m_axi_wdata(m_axi_wdata),
		.m_axi_wstrb(m_axi_wstrb),
		.m_axi_wlast(m_axi_wlast),
		.m_axi_wvalid(m_axi_wvalid),
		.m_axi_wready(m_axi_wready),
		.m_axi_bresp(m_axi_bresp),
		.m_axi_bvalid(m_axi_bvalid),
		.m_axi_bready(m_axi_bready),
		.m_axi_araddr(m_axi_araddr),
		.m_axi_arlen(m_axi_arlen),
		.m_axi_arsize(m_axi_arsize),
		.m_axi_arburst(m_axi_arburst),
		.m_axi_arlock(m_axi_arlock),
		.m_axi_arcache(m_axi_arcache),
		.m_axi_arprot(m_axi_arprot),
		.m_axi_arqos(m_axi_arqos),
		.m_axi_arvalid(m_axi_arvalid),
		.m_axi_arready(m_axi_arready),
		.m_axi_rdata(m_axi_rdata),
		.m_axi_rresp(m_axi_rresp),
		.m_axi_rlast(m_axi_rlast),
		.m_axi_rvalid(m_axi_rvalid),
		.m_axi_rready(m_axi_rready)
	);

reg axi_mem_wenable_r;
reg axi_mem_renable_r;
reg [(C_AXI_ADDR_WIDTH - 1): 0] axi_mem_waddr_base_r;
reg [(C_AXI_ADDR_WIDTH - 1): 0] axi_mem_raddr_base_r;
reg [31: 0] axi_mem_wlen_r;
reg [31: 0] axi_mem_rlen_r;
reg [(C_VECTOR_LEN - 1): 0] axi_mem_wval_r;
reg [(C_VECTOR_LEN - 1): 0] axi_mem_rval_temp_r;

assign AXI_MEM_WENABLE = axi_mem_wenable_r;
assign AXI_MEM_RENABLE = axi_mem_renable_r;
assign AXI_MEM_WADDR_BASE = axi_mem_waddr_base_r;
assign AXI_MEM_RADDR_BASE = axi_mem_raddr_base_r;
assign AXI_MEM_WLEN = axi_mem_wlen_r;
assign AXI_MEM_RLEN = axi_mem_rlen_r;
assign AXI_MEM_WVAL = axi_mem_wval_r;
/********************************************************************/
//AXI Memory Access Instance End
/********************************************************************/

localparam S_WAIT_FOR_START = 	0;
localparam S_START = 			1;
localparam S_PRELOAD = 			2;
localparam S_PRELOAD_DONE = 	3;
localparam S_LOAD_SHIFT = 		4;
localparam S_SHIFT_BUSY = 		5;
localparam S_SHIFT_DONE = 		6;
localparam S_PUSH = 			7;
localparam S_PUSH_BUSY = 		8;
localparam S_PUSH_DONE = 		9;
reg [3: 0] state;

reg [31: 0] bit_shifted;

reg load_en;
reg load_busy;

reg push_en;
reg push_busy;

assign JTAG_LENGTH = (LENGTH == 0) ? 0 :
	   (
		   ((LENGTH - bit_shifted) > C_VECTOR_LEN) ? C_VECTOR_LEN : (LENGTH - bit_shifted )
	   );

always@(posedge m_jtag_aclk or negedge m_jtag_aresetn)
begin
	if (~m_jtag_aresetn)
	begin
		busy_r <= 0;
		jtag_enable_r <= 0;
		jtag_tms_vector_r <= 0;
		jtag_tdi_vector_r <= 0;

		state <= S_WAIT_FOR_START;

		bit_shifted <= 0;

		load_en <= 0;
		push_en <= 0;
		
		axi_mem_wval_r <= 0;
	end
	else
	begin

		case (state)
			S_WAIT_FOR_START:
			begin
				if (~ENABLE)
				begin
					state <= S_START;
				end
			end

			S_START:
			begin
				if (ENABLE)
				begin
					state <= S_PRELOAD;

					busy_r <= 1;
					bit_shifted <= 0;
					load_en <= 1;
				end
			end

			S_PRELOAD:
			begin
				if (load_busy)
				begin
					state <= S_PRELOAD_DONE;

					load_en <= 0;
				end
			end

			S_PRELOAD_DONE:
			begin
				if (~load_busy)
				begin
					state <= S_LOAD_SHIFT;
				end
			end

			S_LOAD_SHIFT:
			begin
				state <= S_SHIFT_BUSY;

				load_en <= ((LENGTH - bit_shifted) > JTAG_LENGTH) ? 1 : 0;
				push_en <= 0;
				jtag_enable_r <= 1;

				jtag_tms_vector_r <= axi_mem_rval_temp_r;
				jtag_tdi_vector_r <= AXI_MEM_RVAL;
			end

			S_SHIFT_BUSY:
			begin
				if (JTAG_BUSY)
				begin
					state <= S_SHIFT_DONE;

					load_en <= 0;
					jtag_enable_r <= 0;
				end
			end

			S_SHIFT_DONE:
			begin
				if (~JTAG_BUSY)
				begin
					state <= S_PUSH;

					bit_shifted <= bit_shifted + JTAG_LENGTH;
				end
			end

			S_PUSH:
			begin
				if ((JTAG_LENGTH > 0) || (~push_busy))
				begin
					state <= (JTAG_LENGTH == 0) ? S_PUSH_BUSY : S_LOAD_SHIFT;

					push_en <= 1;
					
					axi_mem_wval_r <= JTAG_TDO_VECTOR;
				end
			end

			S_PUSH_BUSY:
			begin
				if (push_busy)
				begin
					state <= S_PUSH_DONE;

					push_en <= 0;
				end
			end

			S_PUSH_DONE:
			begin
				if (~push_busy)
				begin
					state <= S_WAIT_FOR_START;

					busy_r <= 0;
				end
			end

			default:
			begin
				state <= S_WAIT_FOR_START;
			end

		endcase
	end
end

wire [31: 0] ADDR_OFS_TOTAL = (LENGTH + 7) / 8;

localparam SLOAD_WAIT_FOR_START = 0;
localparam SLOAD_START = 1;
localparam SLOAD_LOAD1 = 2;
localparam SLOAD_LOAD1_DONE = 3;
localparam SLOAD_LOAD2 = 4;
localparam SLOAD_LOAD2_DONE = 5;
reg [2: 0] sload;
reg [31: 0] sload_addr_ofs;

always@(posedge m_jtag_aclk or negedge m_jtag_aresetn)
begin
	if (~m_jtag_aresetn)
	begin
		load_busy <= 0;
		sload_addr_ofs <= 0;

		axi_mem_renable_r <= 0;
		axi_mem_raddr_base_r <= 0;
		axi_mem_rlen_r <= 0;
		axi_mem_rval_temp_r <= 0;

		sload <= SLOAD_WAIT_FOR_START;
	end
	else
	begin
		if (~ENABLE && ~BUSY)
		begin
			sload <= SLOAD_WAIT_FOR_START;

			load_busy <= 0;
			sload_addr_ofs <= 0;
			axi_mem_renable_r <= 0;
		end
		else
		begin
			case (sload)
				SLOAD_WAIT_FOR_START:
				begin
					if (~load_en)
					begin
						sload <= SLOAD_START;
					end
				end

				SLOAD_START:
				begin
					if (load_en)
					begin
						sload <= SLOAD_LOAD1;

						load_busy <= 1;
						axi_mem_raddr_base_r <= TMS_MEM_ADDR + sload_addr_ofs;
						axi_mem_rlen_r <= ((ADDR_OFS_TOTAL - sload_addr_ofs) > (C_VECTOR_LEN / 8)) ?
									   (C_VECTOR_LEN / 8) : (ADDR_OFS_TOTAL - sload_addr_ofs);
						axi_mem_renable_r <= 1;
					end
				end

				SLOAD_LOAD1:
				begin
					if (AXI_MEM_RBUSY)
					begin
						sload <= SLOAD_LOAD1_DONE;

						axi_mem_renable_r <= 0;
					end
				end

				SLOAD_LOAD1_DONE:
				begin
					if (~AXI_MEM_RBUSY)
					begin
						sload <= SLOAD_LOAD2;

						axi_mem_rval_temp_r <= AXI_MEM_RVAL;

						axi_mem_raddr_base_r <= TDI_MEM_ADDR + sload_addr_ofs;
						axi_mem_renable_r <= 1;
					end
				end

				SLOAD_LOAD2:
				begin
					if (AXI_MEM_RBUSY)
					begin
						sload <= SLOAD_LOAD2_DONE;

						axi_mem_renable_r <= 0;
					end
				end

				SLOAD_LOAD2_DONE:
				begin
					if (~AXI_MEM_RBUSY)
					begin
						sload <= SLOAD_WAIT_FOR_START;

						load_busy <= 0;

						sload_addr_ofs <= sload_addr_ofs + axi_mem_rlen_r;
					end
				end
				
				default:
				begin
					sload <= SLOAD_WAIT_FOR_START;
				end
			endcase
		end
	end
end

localparam SPUSH_WAIT_FOR_START = 0;
localparam SPUSH_START = 1;
localparam SPUSH_PUSH = 2;
localparam SPUSH_PUSH_DONE = 3;
reg [1: 0] spush;
reg [31: 0] spush_addr_ofs;

always@(posedge m_jtag_aclk or negedge m_jtag_aresetn)
begin
	if (~m_jtag_aresetn)
	begin
		push_busy <= 0;
		spush_addr_ofs <= 0;

		axi_mem_wenable_r <= 0;
		axi_mem_waddr_base_r <= 0;
		axi_mem_wlen_r <= 0;

		spush <= SPUSH_WAIT_FOR_START;
	end
	else
	begin
		if (~ENABLE && ~BUSY)
		begin
			spush <= SPUSH_WAIT_FOR_START;

			push_busy <= 0;
			spush_addr_ofs <= 0;
			axi_mem_wenable_r <= 0;
		end
		else
		begin
			case (spush)
				SPUSH_WAIT_FOR_START:
				begin
					if (~push_en)
					begin
						spush <= SPUSH_START;
					end
				end

				SPUSH_START:
				begin
					if (push_en)
					begin
						spush <= SPUSH_PUSH;

						push_busy <= 1;
						axi_mem_waddr_base_r <= TDO_MEM_ADDR + spush_addr_ofs;
						axi_mem_wlen_r <= ((ADDR_OFS_TOTAL - spush_addr_ofs) > (C_VECTOR_LEN / 8)) ?
									   (C_VECTOR_LEN / 8) : (ADDR_OFS_TOTAL - spush_addr_ofs);
						axi_mem_wenable_r <= 1;
					end
				end

				SPUSH_PUSH:
				begin
					if (AXI_MEM_WBUSY)
					begin
						spush <= SPUSH_PUSH_DONE;

						axi_mem_wenable_r <= 0;
					end
				end

				SPUSH_PUSH_DONE:
				begin
					if (~AXI_MEM_WBUSY)
					begin
						spush <= SPUSH_WAIT_FOR_START;

						push_busy <= 0;

						spush_addr_ofs <= spush_addr_ofs + axi_mem_wlen_r;
					end
				end

				default
				begin
					spush <= SPUSH_WAIT_FOR_START;
				end

			endcase
		end
	end
end

endmodule